﻿/************************************************************************************************************************
 * 作者		:	赵劼
 * Blog		:	http://blog.zhaojie.me/2009/03/codetimer.html
 * 创建日期	:	2009年03月10日
 * 日志 :
 *		*****************************************************************************************************************
 *		修改者	:	Loney
 *		邮箱		:	X-0.0-X@Live.cn
 *		修改日期	:	2011年11月1日 12:05
 *		目的 : 
 *			 参照
 *				eaglet	: http://www.cnblogs.com/eaglet/archive/2009/03/10/1407791.html
 *			 	LanceZhang : http://www.cnblogs.com/blodfox777/archive/2010/01/13/codetimer.html
 *			 	新生命开发团队 : http://www.cnblogs.com/nnhy/archive/2011/03/11/codetimer.html
 *			 	......
 *			 进行进一步优化该类.
 *		*****************************************************************************************************************
 *		修改者	:	Loney
 *		邮箱		:	X-0.0-X@Live.cn
 *		修改日期	:	2011年11月4日 9:04
 *		目的 : 
 *			 针对不同的 Windows 操作系统调用不同的 Api :
 *			 	针对 Winodws Vista 系统(包含)以上调用	QueryThreadCycleTime.
 *			 	针对 Winodws Vista 系统以下调用		GetThreadTimes.
 *		*****************************************************************************************************************
 *		修改者	:	Loney
 *		邮箱		:	X-0.0-X@Live.cn
 *		修改日期	:	2011年11月5日 0:42
 *		目的 : 
 *			 增加了多线程并发执行的方法.	-- Pass : 不知道方法是否正确.
 *		*****************************************************************************************************************
 *		修改者	:	Loney
 *		邮箱		:	X-0.0-X@Live.cn
 *		修改日期	:	2012年4月3日 8:21
 *		目的 : 
 *			 将代码中的文本输出提取为资源文件.
 *		*****************************************************************************************************************
 *		修改者	:	Loney
 *		邮箱		:	X-0.0-X@Live.cn
 *		修改日期	:	2012年5月18日 19:18
 *		目的 : 
 *			 精简 Api.
 *			 增加 链式 Api.
 ***********************************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using CodeTime.Properties;

/// <summary>
/// 代码性能计时器.
/// </summary>
static public class CodeTimer {
	#region 链式 Api

	static public ICounterStartSpecification Name(String name) { return new Counter().Name(name); }

	static public ICounterDoSpecification Start(Int32 iteration) { return new Counter().Start(iteration); }

	#region 辅助类

	public class Counter : ICounterExpressionSpecification, ICounterDoSpecification, ICounterStopSpecification, ICounterResultSpecification {
		private String _Name;
		private Int32 _Iteration;
		private CodeTimerResult _CodeTimerResult;

		//public Counter(String name) { _Name = name; }

		//public Counter(Int32 iteration) { _Iteration = iteration; }

		//public Counter(String name, Int32 iteration) {
		//    _Name = name;
		//    _Iteration = iteration;
		//}

		#region ICounterExpression 成员

		public ICounterStartSpecification Name(String name) {
			_Name = name;
			return this;
		}

		#endregion

		#region ICounterStart 成员

		public ICounterDoSpecification Start(Int32 iteration) {
			_Iteration = iteration;
			return this;
		}

		#endregion

		#region ICounterDo 成员

		public ICounterStopSpecification Do(Action action) {
			CodeTimer.Initialize();
			_CodeTimerResult = CodeTimer.Time(_Name, _Iteration, action, null);
			return this;
		}

		#endregion

		#region ICounterStop 成员

		public ICounterResultSpecification Stop() { return this; }

		#endregion

		#region ICounterResult 成员

		public String Elapsed { get { return _CodeTimerResult.TimeElapsed.ToString(); } }

		public CodeTimerResult Result { get { return _CodeTimerResult; } }

		public ICounterExpressionSpecification Print(Action<String> output = null) {
			if (output == null) { output = Console.WriteLine; }
			CodeTimer.Print(_CodeTimerResult, output);
			return this;
		}

		#endregion
	}

	#endregion

	#endregion

	#region 静态 Api

	/*
		 * Initialize 方法应该在测试开始前调用.
		 *	首先它会把当前进程及当前线程的优先级设为最高,这样便可以相对减少操作系统在调度上造成的干扰.
		 *	然后调用一次 Time 方法进行“预热”,让 JIT 将 IL 编译成本地代码,让 Time 方法尽快“进入状态”.
		 *	Time 方法则是真正用于性能计数的方法.
		 * CPU 时钟周期是性能计数中的辅助参考
		 * 	说明 CPU 分配了多少时间片给这段方法来执行,它和消耗时间并没有必然联系.
		 * 	例如 Thread.Sleep 方法会让CPU暂时停止对当前线程的“供给”,这样虽然消耗了时间,但是节省了CPU时钟周期 : 
		 * 		CodeTimer.Time("Thread Sleep", 1, () =&gt; { Thread.Sleep(3000); });
		 * 		CodeTimer.Time("Empty Method", 10000000, () =&gt; { });
		 * 垃圾收集次数的统计,即直观地反应了方法资源分配(消耗)的规模 :
		 * 	Int32 iteration = 100 * 1000;
		 * 	String s = "";
		 * 	CodeTimer.Time("String Concat", iteration, () =&gt; { s += "a"; });
		 * 	StringBuilder sb = new StringBuilder();
		 * 	CodeTimer.Time("StringBuilder", iteration, () =&gt; { sb.Append("a"); });
		 */

	#region 单线程

	/// <summary>
	/// 单线程 Time 方法的初始化.
	/// </summary>
	static public void Initialize() {
		Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
		Thread.CurrentThread.Priority = ThreadPriority.Highest;
		Time("", 1, () => { }, null);
	}

	/// <summary>
	/// 使用单线程的方式执行 <paramref name="action"/>,并使用 Console.WriteLine 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public CodeTimerResult Time(String name, Int32 iteration, Action action) {
		return Time(name, iteration, action, Console.WriteLine);
	}

	/// <summary>
	/// 使用单线程的方式执行 <paramref name="action"/>,并使用 <paramref name="output"/> 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <param name="output">要写入的输出流.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public CodeTimerResult Time(String name, Int32 iteration, Action action, Action<String> output) {
		if (name == null) name = String.Empty;
		if (action == null) { return null; }
		CodeTimerResult result = new CodeTimerResult();

		#region 强制 GC 进行收集,并记录目前各代已经收集的次数.

		GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
		Int32[] gcCounts = new Int32[GC.MaxGeneration + 1];
		for (Int32 i = 0; i <= GC.MaxGeneration; i++) {
			gcCounts[i] = GC.CollectionCount(i);
		}

		#endregion

		#region 执行代码,记录下消耗的时间及 CPU 时钟周期.

		Stopwatch watch = new Stopwatch();
		watch.Start();
		UInt64 cycleCount = GetCycleCount();
		for (Int32 i = 0; i < iteration; i++) {
			action();
		}
		UInt64 cpuCycles = GetCycleCount() - cycleCount;
		watch.Stop();

		#endregion

		#region 收集数据

		result.Name = String.Format(Resources.SingleThreadName, name, Resources.NameSuffix);
		result.TimeElapsed = watch.ElapsedMilliseconds;
		result.CPUCycles = cpuCycles;
		for (Int32 i = 0; i <= GC.MaxGeneration; i++) {
			Int32 count = GC.CollectionCount(i) - gcCounts[i];
			result.GenerationList[i] = count;
		}
		result.ThreadCount = 1;
		result.Iteration = iteration;
		Print(result, output);

		#endregion

		return result;
	}

	#endregion

	#region 多线程

	/// <summary>
	/// 使用多线程并发的方式执行 <paramref name="action"/>,并使用 Console.WriteLine 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="threadCount">线程的计数.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public List<CodeTimerResult> Time(String name, Int32 iteration, Int32 threadCount, Action action) {
		return Time(name, iteration, threadCount, 0, action, Console.WriteLine);
	}

	/// <summary>
	/// 使用多线程并发的方式执行 <paramref name="action"/>,并使用 <paramref name="output"/> 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="threadCount">线程的计数.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <param name="output">要写入的输出流.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public List<CodeTimerResult> Time(String name, Int32 iteration, Int32 threadCount, Action action, Action<String> output) {
		return Time(name, iteration, threadCount, 0, action, output);
	}

	/// <summary>
	/// 使用多线程并发的方式执行 <paramref name="action"/>,并使用 Console.WriteLine 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="threadCount">线程的计数.</param>
	/// <param name="mockThinkTime">模拟思考的时间.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public List<CodeTimerResult> Time(String name, Int32 iteration, Int32 threadCount, Int32 mockThinkTime, Action action) {
		return Time(name, iteration, threadCount, 0, action, Console.WriteLine);
	}

	/// <summary>
	/// 使用多线程并发的方式执行 <paramref name="action"/>,并使用 <paramref name="output"/> 方法输出 : 方法的运行时间, Cpu 时钟周期, 各代垃圾收集的回收次数的统计.
	/// </summary>
	/// <param name="name">名称.</param>
	/// <param name="iteration">循环次数.</param>
	/// <param name="threadCount">线程的计数.</param>
	/// <param name="mockThinkTime">模拟思考的时间.</param>
	/// <param name="action">需要执行的方法体.</param>
	/// <param name="output">要写入的输出流.</param>
	/// <remarks>一个 <see cref="CodeTimerResult"/> 表示执行的结果.</remarks>
	static public List<CodeTimerResult> Time(String name, Int32 iteration, Int32 threadCount, Int32 mockThinkTime, Action action, Action<String> output) {
		if (name == null) name = String.Empty;
		if (mockThinkTime < 0) throw new ArgumentOutOfRangeException("thinkTime", Resources.MessageThan0);

		List<CodeTimerResult> results = new List<CodeTimerResult>(threadCount);
		CodeTimerResult totalResult = new CodeTimerResult();

		ManualResetEvent manualResetEvent = new ManualResetEvent(true);		// 主线程控制信号
		ManualResetEvent threadResetEvent = new ManualResetEvent(true); 	// 子线程线程控制信号
		Int32 currentThreadIndex;											// 当前线程索引值

		for (Int32 repeat = 0; repeat < iteration; repeat++) {
			manualResetEvent.Reset();										// 主线程进入阻止状态
			threadResetEvent.Reset();										// 子线程进入阻止状态
			currentThreadIndex = 0;

			for (Int32 i = 0; i < threadCount; i++) {
				Thread thread = new Thread(
					(threadIndex) => {
						CodeTimerResult TimeResult = new CodeTimerResult();
						Interlocked.Increment(ref currentThreadIndex);
						if (currentThreadIndex < threadCount) {
							threadResetEvent.WaitOne();			// 等待所有线程创建完毕后同时执行测试
						} else {
							threadResetEvent.Set();				// 最后一个线程创建完成,通知所有线程,开始执行测试
						}

						// 执行测试,委托给 SingleThreadTime 方法来做.
						TimeResult = Time(
							String.Format(
								Resources.ChildThreadName,
								name,
								threadIndex,
								Resources.NameSuffix
							),
							1,
							action,
							null
						);

						Interlocked.Decrement(ref currentThreadIndex);

						if (currentThreadIndex == 0) {
							results.Add(TimeResult);			// 保存执行结果
							manualResetEvent.Set();				//通知主线程继续
						} else {
							results.Add(TimeResult);			// 保存执行结果
						}
					}
				);

				thread.Start(i);
			}

			// 阻止主线程,等待子线程完成所有任务.
			manualResetEvent.WaitOne();
			Thread.Sleep(mockThinkTime);
		}

		totalResult.Name = String.Format(Resources.MultiThreadName, name, Resources.NameSuffix);
		results.ForEach(
			(item) => {
				totalResult.TimeElapsed += item.TimeElapsed;
				totalResult.CPUCycles += item.CPUCycles;
				for (Int32 i = 0; i < totalResult.GenerationList.Length; i++) {
					totalResult.GenerationList[i] += item.GenerationList[i];
				}
			}
		);

		totalResult.ThreadCount = threadCount;
		totalResult.Iteration = iteration;
		Print(totalResult, output);

		// 释放资源
		manualResetEvent.Close();
		threadResetEvent.Close();

		return results;
	}

	#endregion

	/// <summary>
	/// 打印 <paramref name="result"/> 到 <paramref name="output"/>.
	/// </summary>
	/// <param name="result"><see cref="CodeTimer"/> 的执行结果.</param>
	/// <param name="output">要写入的输出流</param>
	static public void Print(this CodeTimerResult result, Action<String> output) {
		if (output != null) {
			ConsoleColor currentForeColor = Console.ForegroundColor;	// 保留当前控制台前景色,并使用黄色输出名称参数.
			Console.ForegroundColor = ConsoleColor.Yellow;
			if (String.IsNullOrWhiteSpace(result.Name)) result.Name = Resources.NameSuffix;
			output(result.Name);
			Console.ForegroundColor = currentForeColor;					// 恢复控制台默认前景色,并打印出消耗时间及CPU时钟周期.
			output(String.Format(Resources.TimeElapsed, result.TimeElapsed.ToString("N0")));
			output(String.Format(Resources.CPUCycles, result.CPUCycles.ToString("N0")));

			// 打印执行过程中各代垃圾收集回收次数.
			for (Int32 i = 0; i < result.GenerationList.Length; i++) {
				output(String.Format(Resources.CPUGen, i, result.GenerationList[i]));
			}

			// 循环次数 线程的计数 模拟思考的时间
			String stats = String.Format(Resources.IterationCount, result.Iteration);
			if (result.ThreadCount > 1) { stats = stats + String.Format(Resources.ThreadCount, result.ThreadCount); }
			if (result.MockThinkTime > 0) { stats = stats + String.Format(Resources.MockThinkTime, result.MockThinkTime); }
			output(stats);
		}
	}

	#region 辅助方法

	#region P/Invoke

	// 统计 CPU 时钟周期时.
	[DllImport("kernel32.dll", SetLastError = true)]
	static extern Boolean GetThreadTimes(
		IntPtr hThread,
		out UInt64 lpCreationTime,
		out UInt64 lpExitTime,
		out UInt64 lpKernelTime,
		out UInt64 lpUserTime
	);

	// 统计 CPU 时钟周期时.(Vista 版本以上新的函数)
	[DllImport("kernel32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	static extern Boolean QueryThreadCycleTime(IntPtr threadHandle, ref UInt64 cycleTime);

	[DllImport("kernel32.dll")]
	static extern IntPtr GetCurrentThread();

	#endregion

	static private UInt64 GetCycleCount() {
		UInt64 cycleCount = 0;
		if (EnvironmentHelper.IsGreaterThanOrEqualToVista) {			// 针对 Winodws Vista 系统(包含)以上进行新的 Api 调用.
			QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
		} else {
			UInt64 l;
			UInt64 kernelTime, userTimer;
			GetThreadTimes(GetCurrentThread(), out l, out l, out kernelTime, out userTimer);
			cycleCount = kernelTime + userTimer;
		}

		return cycleCount;
	}

	#endregion

	#endregion
}

/// <summary>
/// 表示 <see cref="CodeTimer"/> 执行结果的类.
/// </summary>
public class CodeTimerResult {
	/// <summary>
	/// 初始化 <see cref="CodeTimer"/> 类的新实例.
	/// </summary>
	public CodeTimerResult() {
		GenerationList = new Int32[GC.MaxGeneration + 1];
	}

	/// <summary>
	/// 名称.
	/// </summary>
	public String Name { get; set; }

	/// <summary>
	/// 运行时间.
	/// </summary>
	public Int64 TimeElapsed { get; set; }

	/// <summary>
	/// Cpu 时钟周期.
	/// </summary>
	public UInt64 CPUCycles { get; set; }

	/// <summary>
	/// GC 代数集合.
	/// </summary>
	public Int32[] GenerationList { get; set; }

	/// <summary>
	/// 线程的计数.
	/// </summary>
	public Int32 ThreadCount { get; set; }

	/// <summary>
	/// 重复的次数.
	/// </summary>
	public Int32 Iteration { get; set; }

	/// <summary>
	/// 模拟思考的时间.
	/// </summary>
	public Int32 MockThinkTime { get; set; }

	/// <summary>
	/// 重置 <see cref="CodeTimer"/>.
	/// </summary>
	/// <returns>重置后的 <see cref="CodeTimer"/> 对象实例.</returns>
	public CodeTimerResult Reset() {
		Name = String.Empty;
		TimeElapsed = 0;
		CPUCycles = 0;
		GenerationList = new Int32[GC.MaxGeneration + 1];

		return this;
	}
}

#region Interfaces

public interface ICounterExpressionSpecification : ICounterStartSpecification {
	ICounterStartSpecification Name(String name);
}

public interface ICounterStartSpecification {
	ICounterDoSpecification Start(Int32 iteration);
}

public interface ICounterDoSpecification {
	ICounterStopSpecification Do(Action action);
}

public interface ICounterStopSpecification {
	ICounterResultSpecification Stop();
}

public interface ICounterResultSpecification {
	/// <summary>
	/// 返回计时器经过时间(单位 : 秒)
	/// </summary>
	String Elapsed { get; }

	CodeTimerResult Result { get; }

	ICounterExpressionSpecification Print(Action<String> output = null);
}

#endregion